<?php
/*++
 *功能：socket操作类
 *作者：gsj
 *时间：2012.03.25
 *描述：客户端socket类
 *文件名称：socket.class.php
--*/
class Socket {
	var $host ;	//要连接的主机IP地址
	var $port ;	//连接端口号
	var $Error = array(); //保存错误信息
	var $socket = NULL;	//连接标识
	var $queryStr = ''; //发送的数据
	var $timeout_send = 0;  //socket连接发送数据的超时时间
	var $timeout_recv = 0;  //socket连接接收数据的超时时间
	
	/*++
	 *函数名：Socket
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：构造函数,初始化相关成员函数
	 *参数：$sort socket类型
	 		$host 主机
	 		$port 端口号
	 		$timeout_send 发送超时时间
	 		$timeout_recv 接收超时时间
	 *返回值：无
	--*/
	function Socket($sort,$host,$port,$timeout_send=3,$timeout_recv=10){
	    register_shutdown_function(array(&$this,'__destruct'));
		if(!extension_loaded("sockets")){
			$this->halt("请打开socket相关扩展");
		}
		if(empty($host)) $this->halt("请输入有效目标地址");
		if(empty($port)) $this->halt("请输入有效的端口");
		$this->host = $host;
		$this->port = $port;
        $this->timeout_send = $timeout_send;
        $this->timeout_recv = $timeout_recv;
		if($sort == "tcp"){
			$this->CreateSocket();
		}elseif($sort == "udp"){
			$this->CreateUdpSocket();
		}
	}
	
	/*++
	 *函数名：CreateSocket
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：创建socket连接
	 *参数：无
	 *返回值：成功返回连接资源，失败返回false
	--*/
	function CreateSocket(){
		!$this->socket&&$this->socket=socket_create(AF_INET,SOCK_STREAM,SOL_TCP);//创建socket
		if($this->socket){
			socket_set_option($this->socket,SOL_SOCKET,SO_SNDTIMEO,array("sec"=>$this->timeout_send,"usec"=>0));
			socket_set_option($this->socket,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>$this->timeout_recv,"usec"=>0));
		}
		$r = @socket_connect($this->socket,$this->host,$this->port);
		if($r){
			return $r;
		}else{
			$this->error[] = socket_last_error($this->socket);
			return false;
		}
	}
	
	/*++
	 *函数名：sendMsg
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：向socket服务器写入数据
	 *参数：$contents   要发送的内容
	 *返回值：成功则返回发送的字节数，失败返回false
	--*/
	function sendMsg($contents){

		$this->queryStr = $contents;
		!$this->socket&&$this->CreateSocket();
		$contents = $this->fliterSendData($contents);
		$result = @socket_write($this->socket,$contents,strlen($contents));
		if(!intval($result)){
			$this->error[] = socket_last_error($this->socket);
			return false;
		}else{
			return $result;
		}	
	}
	
	/*++
	 *函数名：getMsg
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：读取连接中得数据
	 *参数：$len   要读取的长度
	 *返回值：成功返回读取到的数据，失败返回false
	--*/
	function getMsg($len){
		$response = socket_read($this->socket,$len);
		if(false === $response){
			$this->error[] = socket_last_error($this->socket);
			return false;
		}
		return $response;
	}
	
	/*++
	 *函数名：CreateUdpSocket
	 *作者：gsj
	 *时间：2012.07.26
	 *描述：创建socket连接
	 *参数：无
	 *返回值：成功则返回创建的socket连接，否则返回false
	--*/
	function CreateUdpSocket(){
		!$this->socket&&$this->socket=@socket_create(AF_INET,SOCK_DGRAM,SOL_UDP);//创建socket
		if($this->socket){
			return $this->socket;
		}else{
			$this->error[] = socket_last_error($this->socket);
			return false;
		}
	}
	
	/*++
	 *函数名：sendUdpMsg
	 *作者：gsj
	 *时间：2012.07.26
	 *描述：发送UDP消息
	 *参数：要发送的内容
	 *返回值：成功则返回true，否则返回false
	--*/
	function sendUdpMsg($str){
		!$this->socket&&$this->CreateUdpSocket();
		$len = strlen($str);
		if(false !== @socket_sendto($this->socket,$str,$len,0,$this->host,$this->port)){
			return true;
		}else{
			return false;
		}
	}
	
	/*++
	 *函数名：sendbroadcastMsg
	 *作者：gsj
	 *时间：2012.07.26
	 *描述：发送广播包
	 *参数：要发送的内容
	 *返回值：成功则返回true，否则返回false
	--*/
	function sendbroadcastMsg($str) {
		!$this->socket&&$this->CreateUdpSocket();
		socket_set_option($this->socket,SOL_SOCKET,SO_BROADCAST,true);
		$len = strlen($str);
		if($len == @socket_sendto($this->socket,$str,$len,0,$this->host,$this->port)){
			return true;
		}else{
			return false;
		}
	}
	
	//
	/*++
	 *函数名：intTo8Byte
	 *作者：gsj
	 *时间：2012.08.26
	 *描述：将字符型代表的整数 分解为高4字节和低4字节 ，以适应C中long long型的数据
	 *参数：字符串形式的   数值变量
	 *返回值：返回存放高低位结果的数组
	--*/
	function intTo8Byte($number)
	{
        $maxInt = strval(pow(2, 32));
        $maxInt = gmp_strval(gmp_sub($maxInt, '1'));
        //if(gmp_cmp($maxInt,$number) > 0 ){
        //	return array("high"=>0,"low"=>$number);
        //}
        $high = gmp_div_q($number, $maxInt); //前四个字节移动到后四个字节
        $high = gmp_strval($high); //得到前四个字节的整数的字符串形式
        
        $low = gmp_and($number, $maxInt); // 前四个字节置0，得到后四个字节的整数值
        $low = gmp_strval($low);
        //return pack('NN', $high, $low) ; //将前四字节后和后四字节打包，得到长度为8字节的整数
        return array("high"=>$high,"low"=>$low);
	}
	/*++
	 *函数名：fliterSendData
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：对发送的数据进行过滤
	 *参数：$contents   要发送的内容
	 *返回值：过滤后的内容
	--*/
	function fliterSendData($contents){
		//可以写自己的对写入的数据过滤代码
		return $contents;
	}
	
	/*++
	 *函数名：getError
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：取得所有错误信息
	 *参数：无
	 *返回值：错误信息
	--*/
	function getError(){
		return $this->error;
	}
	
	/*++
	 *函数名：getLastError
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：最后一次错误信息
	 *参数：无
	 *返回值：最后一次错误信息
	--*/
	function getLastError(){
		return $this->error(count($this->error));
	}
	
	/*++
	 *函数名：getLastMsg
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：获取最后一次发送的消息
	 *参数：无
	 *返回值：获取最后一次发送的消息
	--*/
	function getLastMsg(){
		return $this->queryStr;
	}
	
	/*++
	 *函数名：getHost
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：获取连接主机的地址
	 *参数：无
	 *返回值：获取连接主机的地址
	--*/
	function getHost(){
		return $this->host;
	}
	
	/*++
	 *函数名：getPort
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：获取要连接的端口号
	 *参数：无
	 *返回值：获取要连接的端口号
	--*/
	function getPort(){
		return $this->port;
	}
	
	//错误提示函数
	function halt($msg){
		die($msg);
	}
	
	/*++
	 *函数名：close
	 *作者：gsj
	 *时间：2012.03.25
	 *描述：关闭socket连接
	 *参数：无
	 *返回值：无
	--*/
	function close(){
		$this->socket&&socket_close($this->socket);
		$this->socket=NULL; //资源初始化
	}
	
	//析构函数
	function __destruct(){
		$this->close();
	}
}
?>
